package com.gitee.deeround.javaprotect.tool;

import com.gitee.deeround.javaprotect.tool.util.*;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;

import java.io.File;
import java.util.List;

public class Encode {
    private static IEncrypt encrypt = new Encrypt();

    public static void run(String from, String to, String includeJarCondition, String includeClassCondition) {
        String targetDir = CommonUtils.getJarUnJarPath(from);
        List<JarFileInfo> files = JarUtils.unJar(1, from, includeJarCondition);
        LogUtils.info("jar释放完毕,开始加密....");
        ClassPool pool = Context.Instance.Pool;
        CommonUtils.loadClassPath(pool, files);
        for (JarFileInfo file : files) {
            String fileNameLower = file.getFileName().toLowerCase();
            if (fileNameLower.endsWith(".class")) {
                if (!StringUtils.hitCondition(includeClassCondition, file.getClassName())) {
                    continue;
                }
                String newFile = CommonUtils.getClassEncodePath(targetDir, encrypt.eClassName(file.getClassName()));
                FileUtils.deleteFile(newFile);
                FileUtils.createDirectory(newFile);
                byte[] data = FileUtils.toBytes(new File(file.FilePath));
                FileUtils.saveStream(encrypt.e(data), newFile);
                clearMethod(pool, file);
                LogUtils.info("加密类:" + file.getClassName());
            } else if (fileNameLower.endsWith(".yml") || fileNameLower.endsWith(".yaml") || fileNameLower.endsWith(".properties")) {

            }
        }
        LogUtils.info("类加密完毕!");
        JarUtils.zipJar(targetDir, to);
        LogUtils.info("重写生成jar完毕!路径:" + to);

        String agentKeyPath = (new File(to)).getParent().concat(File.separator).concat("protect.agent.key");
        buildAgentKey(agentKeyPath);
        LogUtils.info("生成AgentKey完毕!路径:" + agentKeyPath);
        LogUtils.info("加密已成功完成！！！！");
    }

    private static void clearMethod(ClassPool pool, JarFileInfo file) {
        try {
            String msg = "System.out.println(\"" + Context.Instance.getCopyright() + "\");";
            CtClass ctClass = pool.getOrNull(file.getClassName());
            if (ctClass == null) {
                LogUtils.error("无法清除类方法:" + file.getFileName());
            } else {
                for (CtMethod method : ctClass.getDeclaredMethods()) {
                    CtClass returnType = method.getReturnType();
                    if (returnType.equals(CtClass.booleanType)) {
                        method.setBody("{" + msg + "return false;}");
                    } else if (returnType.equals(CtClass.charType)) {
                        method.setBody("{" + msg + "return 'a';}");
                    } else if (returnType.equals(CtClass.byteType)) {
                        method.setBody("{" + msg + "return 0;}");
                    } else if (returnType.equals(CtClass.shortType)) {
                        method.setBody("{" + msg + "return 0;}");
                    } else if (returnType.equals(CtClass.intType)) {
                        method.setBody("{" + msg + "return 0;}");
                    } else if (returnType.equals(CtClass.longType)) {
                        method.setBody("{" + msg + "return 0;}");
                    } else if (returnType.equals(CtClass.floatType)) {
                        method.setBody("{" + msg + "return 0;}");
                    } else if (returnType.equals(CtClass.doubleType)) {
                        method.setBody("{" + msg + "return 0;}");
                    } else if (returnType.equals(CtClass.voidType)) {
                        method.setBody("{" + msg + "}");
                    } else {
                        method.setBody("{" + msg + "return null;}");
                    }
                }
                FileUtils.saveStream(ctClass.toBytecode(), file.FilePath);
            }
        } catch (Exception e) {
            throw new ProtectException(e);
        }
    }

    private static void buildAgentKey(String path) {
        FileUtils.writeAllText(path, encrypt.eKey());
    }

}
